<?php

declare(strict_types=1);

namespace app\attachment\controller;

use app\admin\BaseAdminController;
use think\facade\View;
use think\facade\Request;
use app\attachment\model\Category;
use app\system\model\LogOperate as Log;
use think\facade\Filesystem;
use think\facade\Config;
use app\attachment\model\Attachment;
use think\facade\Env;

class Index extends BaseAdminController
{
    /**
     * 文件类型
     * @var string
     */
    private $typeKey = 'type';

    /**
     * 文件名|其他名称
     * @var string
     */
    private $nameKey = 'name';

    /**
     * 分类ID
     * @var string
     */
    private $cidKey = 'cat_id/d';

    /**
     * 文件上传页面
     */
    public function index()
    {
        $multiple = input('multiple/d', 0);
        $callback = input('callback', 'receiveData');
        View::assign('title', '附件管理');
        View::assign('height', $this->isFrame ? 190 : 250);
        View::assign('multiple', $multiple);
        View::assign('callback', $callback);
        View::assign('addJs', addJs(['confirm', 'jstree']));
        return View::fetch();
    }

    /**
     * ajax 读取分类
     * @return string
     */
    public function ajax_category()
    {
        $type = input($this->typeKey);
        $tree = Category::where('type', $type)->order(['id' => 'asc'])->select();
        return generateOptions($tree);
    }

    /**
     * 文件归类
     */
    public function move()
    {
        $id = input('id');
        $pid = input('pid/d', 0);
        if ($id && $pid) {
            $arr = explode(',', $id);
            $list = Attachment::whereIn('id', $arr)->select();
            foreach ($list as $value) {
                $value->cat_id = $pid;
                $value->save();
            }
        }
        return json(['msg' => '']);
    }

    /**
     * 删除文件
     */
    public function delete()
    {
        $id = input('id');
        if ($id) {
            $arr = explode(',', $id);
            $list = Attachment::whereIn('id', $arr)->select();
            $new = [];
            foreach ($list as $value) {
                $pre = Config::get('filesystem.disks.public.root');
                $url = $pre . '/' . $value['url'];
                $new[] = [
                    '地址' => $value['url'],
                    '文件名' => $value['filename'],
                ];
                if (file_exists($url)) {
                    unlink($url);
                }
                $value->delete();
            }
            Log::add('删除文件', $new);
        }
        return json(['msg' => '']);
    }

    /**
     * 文件重命名
     */
    public function rename()
    {
        $id = input('id/d', 0);
        $name = input($this->nameKey);
        if ($id && $name) {
            $obj = Attachment::find($id);
            if ($obj) {
                $old = $obj->filename;
                $obj->filename = trim($name);
                $obj->save();
                Log::add('上传文件', [
                    '地址' => $obj->url,
                    '旧文件名' => $old,
                    '新文件名' => $name,
                ]);
            }
        }
        return json(['msg' => '']);
    }

    /**
     * 读取文件列表
     */
    public function data()
    {
        $offset = input('offset/d');
        $limit = input('limit/d');
        $cat_id = input($this->cidKey, 0);
        $type = input($this->typeKey, 'image');
        $keywords = input('keywords');
        $obj = new Attachment();
        $obj = $obj->where('type', $type);
        if ($cat_id) {
            $arr = Category::getCatId($cat_id);
            $obj = $obj->whereIn('cat_id', $arr);
        }
        if ($keywords) {
            $obj = $obj->where('filename', 'like', '%' . $keywords . '%');
        }
        $total = $obj->count();
        $list = $obj->order('id', 'desc')->limit($offset, $limit)->select();
        $new = [];
        $arr = Category::where('type', $type)->column('name', 'id');
        $pre = Config::get('filesystem.disks.public.url');
        foreach ($list as $value) {
            $info = new \SplFileInfo($value['url']);
            $value['filesize'] = formatBytes($value['filesize']);
            $value['cat_name'] = $arr[$value['cat_id']] ?? '-';
            $value['url'] = $pre . '/' . $value['url'];
            $value['icon'] = '/static/attachment/extensions/' . $info->getExtension() . '.png';
            $new[] = $value;
        }
        return json([
            'total' => $total,
            'rows' => $new,
        ]);
    }

    /**
     * 获取文件类型名称
     * @param string $type
     * @return string
     */
    private function getTypeName($type)
    {
        $arr = [
            'image' => '图片',
            'video' => '视频',
            'file' => '文件',
        ];
        return $arr[$type];
    }

    /**
     * 文件上传页面
     */
    public function upload()
    {
        $type = input($this->typeKey, 'image');
        $cat_id = input($this->cidKey, 0);
        Env::load(app_path() . '/config/.env');
        View::assign('title', '上传文件');
        View::assign('chunk_size', Env::get('chunk_size'));
        View::assign('max_file_size', Env::get('max_file_size'));
        View::assign('extensions', Env::get('extensions'));
        View::assign('type', $type);
        View::assign('cat_id', $cat_id);
        View::assign('addJs', addJs(['plupload']));
        return View::fetch();
    }

    /**
     * 文件上传
     */
    public function upload_file()
    {
        $type = input($this->typeKey, 'other');
        if (!in_array($type, ['image', 'video', 'file', 'other'])) {
            return json(['msg' => '类型错误']);
        }
        $chunk = input('chunk/d');
        $chunks = input('chunks/d');
        $file = Request::file('file');
        $mimetype = $file->getOriginalMime();
        if ($chunks == 1) {
            // 单文件上传
            $filename = Filesystem::putFile($type, $file);
            $this->addFileData($filename, $_FILES['file']['name'], $mimetype, $_FILES['file']['size'], $type);
        } else {
            // 切片上传
            // 错误的文件名
            $name = input($this->nameKey);
            $specialChars = "#@$%^&*()_+-=[]{}`~<>?,/\\";
            $name = str_replace(str_split($specialChars), '', $name);
            // 返回文件名
            $info = new \SplFileInfo($name);
            $extension = $info->getExtension();
            $filename = $type . '/' . $file->hashName() . '.' . $extension;
            // 创建目录
            $path = Config::get('filesystem.disks.public.root') . '/';
            $dir = pathinfo($path . $filename, PATHINFO_DIRNAME);
            if (!is_dir($dir)) {
                mkdir($dir, 0777, true);
            }
            // 读取并写入文件
            $data = file_get_contents($_FILES['file']['tmp_name']);
            $tmp = $dir . '/' . $name;
            file_put_contents($tmp, $data, FILE_APPEND);
            if ($chunk == $chunks - 1) {
                // 重命名
                rename($tmp, $path . $filename);
                $this->addFileData($filename, $name, $mimetype, filesize($path . $filename), $type);
            }
        }
        return json(['msg' => '']);
    }

    /**
     * 新增文件数据
     * @param string $url
     * @param string $name
     * @param string $mimetype
     * @param int $size
     * @param string $type
     */
    private function addFileData($url, $name, $mimetype, $size, $type)
    {
        $cat_id = input($this->cidKey, 0);
        $obj = new Attachment;
        $obj->cat_id = $cat_id;
        $obj->admin_id = UID;
        $obj->dept_id = $this->user->dept_id;
        $obj->url = $url;
        $obj->filename = $name;
        $obj->mimetype = $mimetype;
        $obj->filesize = $size;
        $obj->type = $type;
        $obj->save();
        Log::add('上传文件', [
            '地址' => $url,
            '类型' => $this->getTypeName($type),
            '大小' => formatBytes($size),
        ]);
        // 上传事件
        $this->trigger('UploadFile', $obj);
    }

    /**
     * 读取分类列表
     */
    public function cateogry()
    {
        $type = input($this->typeKey);
        $default = [
            [
                'id' => '0',
                'text' => '全部' . $this->getTypeName($type),
                'state' => ['selected' => true],
                'icon' => 'mdi mdi-folder-outline',
                'children' => [],
            ]
        ];
        $data = Category::where('type', $type)->order(['id' => 'asc'])->select();
        $tree = generateTree($data);
        array_unshift($tree, ...$default);
        return json($tree);
    }

    /**
     * 新增分类
     */
    public function add()
    {
        $id = input('id');
        $name = input($this->nameKey);
        $type = input($this->typeKey);
        if (empty($name)) {
            return json(['msg' => '请输入分类名称']);
        }
        $obj = new Category;
        $obj->pid = $id;
        $obj->admin_id = UID;
        $obj->name = trim($name);
        $obj->update_by = USERNAME;
        $obj->type = $type;
        $obj->dept_id = $this->user->dept_id;
        $obj->save();
        Log::add('新增' . $this->getTypeName($type) . '分类', [
            '分类名称' => $name
        ]);
        return json([]);
    }

    /**
     * 编辑分类
     */
    public function edit()
    {
        $id = input('id');
        $name = input($this->nameKey);
        $type = input($this->typeKey);
        if (empty($name)) {
            return json(['msg' => '请输入分类名称']);
        }
        $obj = Category::find($id);
        if (empty($obj)) {
            return json(['msg' => '分类不存在']);
        }
        $obj->name = trim($name);
        $obj->update_by = USERNAME;
        $obj->save();
        Log::add('编辑' . $this->getTypeName($type) . '分类', [
            'ID' => $id,
            '分类名称' => $name
        ]);
        return json(['msg' => '']);
    }

    /**
     * 删除分类
     */
    public function remove()
    {
        $id = input('id');
        $type = input($this->typeKey);
        $errorMsg = '';
        if ($id == 0) {
            $errorMsg = '根分类无法删除';
        } elseif (Category::where('pid', $id)->count() > 0) {
            $errorMsg = '请先删除下级分类';
        } else {
            $obj = Category::find($id);
            if (empty($obj)) {
                $errorMsg = '分类不存在';
            } else {
                $obj->delete();
                Log::add('删除' . $this->getTypeName($type) . '分类', [
                    'ID' => $id,
                    '分类名称' => $obj->name
                ]);
            }
        }
        return json(['msg' => $errorMsg]);
    }
}
